#include "spi.h"
#include "w25qxx.h"  


 
u16 W25QXX_ID;	 
													 

void W25QXX_Init(void)
{ 
  /*
  // for qiming MCU
  GPIO_InitTypeDef  GPIO_InitStructure;
 
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); 

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;             //PG8
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;         
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;    
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;          
  GPIO_Init(GPIOG, &GPIO_InitStructure);                
	*/
	  GPIO_InitTypeDef  GPIO_InitStructure;
 
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);

	  //GPIOB14
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_Init(GPIOG, &GPIO_InitStructure);
 
	GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7 output 1, prevent NRF from interfering with SPI FLASH communication 

	W25QXX_CS=1;			                                    
	SPI1_Init();		   			                              
	SPI1_Setclock(SPI_BaudRatePrescaler_2);		            
	W25QXX_ID=W25QXX_ReadID();	                          
}  


u8 W25QXX_ReadSR(void)   
{  
	u8 byte=0;   
	W25QXX_CS=0;                                     
	SPI1_ReadWriteByte(W25X_CMD_ReadStatusReg);      
	byte=SPI1_ReadWriteByte(0Xff);                  
	W25QXX_CS=1;                                       
	return byte;   
} 

void W25QXX_Write_SR(u8 sr)   
{   
	W25QXX_CS=0;                                  
	SPI1_ReadWriteByte(W25X_CMD_WriteStatusReg);    
	SPI1_ReadWriteByte(sr);                       
	W25QXX_CS=1;                                     	      
} 

	 
void W25QXX_Write_Enable(void)   
{
	W25QXX_CS=0;                                  
  SPI1_ReadWriteByte(W25X_CMD_WriteEnable);   
	W25QXX_CS=1;                                   	      
} 

void W25QXX_Wait_Busy(void)   
{   
	while((W25QXX_ReadSR()&0x01)==0x01);      
} 
	 
void W25QXX_Write_Disable(void)   
{  
	W25QXX_CS=0;                                   
  SPI1_ReadWriteByte(W25X_CMD_WriteDisable);     
	W25QXX_CS=1;                                   	      
} 

u16 W25QXX_ReadID(void)
{
	u16 IDnum = 0;	  
	W25QXX_CS=0;				    
	SPI1_ReadWriteByte(0x90); 	    
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	    
	SPI1_ReadWriteByte(0x00); 	 			   
	IDnum|=SPI1_ReadWriteByte(0xFF)<<8;  
	IDnum|=SPI1_ReadWriteByte(0xFF);	 
	W25QXX_CS=1;				    
	return IDnum;
} 

void W25QXX_Erase_Chip(void)   
{                                   
    W25QXX_Write_Enable();                   
    W25QXX_Wait_Busy();   
  	W25QXX_CS=0;                                
    SPI1_ReadWriteByte(W25X_CMD_ChipErase);   
	  W25QXX_CS=1;                                  	      
	  W25QXX_Wait_Busy();   				           
}  

void W25QXX_Erase_Sector(u32 First_Addr)   
{    
 	  First_Addr*=4096;
    W25QXX_Write_Enable();                  
    W25QXX_Wait_Busy();   
  	W25QXX_CS=0;                                     
    SPI1_ReadWriteByte(W25X_CMD_SectorErase);     
    SPI1_ReadWriteByte((u8)((First_Addr)>>16));      
    SPI1_ReadWriteByte((u8)((First_Addr)>>8));   
    SPI1_ReadWriteByte((u8)First_Addr);  
	  W25QXX_CS=1;                                      	      
    W25QXX_Wait_Busy();   				                
}  

void W25QXX_PowerDown(void)   
{ 
  	W25QXX_CS=0;                              
    SPI1_ReadWriteByte(W25X_CMD_PowerDown);    
	  W25QXX_CS=1;                                  	      
    delay_us(3);                             
}   

void W25QXX_WAKEUP(void)   
{  
  	W25QXX_CS=0;                                         
    SPI1_ReadWriteByte(W25X_CMD_ReleasePowerDown);    
	  W25QXX_CS=1;                                          	      
    delay_us(3);                                     
}   


void W25QXX_Read(u8* DataBuffer,u32 StartAddress,u16 ByteCount)   
{  										    
	W25QXX_CS=0;                                    
    SPI1_ReadWriteByte(W25X_CMD_ReadData);        
    SPI1_ReadWriteByte((u8)((StartAddress)>>16));     
    SPI1_ReadWriteByte((u8)((StartAddress)>>8));   
    SPI1_ReadWriteByte((u8)StartAddress);   
	
			while (ByteCount--) 
			{
					*DataBuffer = SPI1_ReadWriteByte(0XFF);
					DataBuffer++;
			}		
	W25QXX_CS=1;  				    	      
}  

void W25QXX_Write_Page(u8* DataBuffer,u32 StartAddress,u16 ByteCount)
{  
	W25QXX_Write_Enable();                   
	  W25QXX_CS=0;                                     
    SPI1_ReadWriteByte(W25X_CMD_PageProgram);        
    SPI1_ReadWriteByte((u8)((StartAddress)>>16));      
    SPI1_ReadWriteByte((u8)((StartAddress)>>8));   
    SPI1_ReadWriteByte((u8)StartAddress);   
		while (ByteCount--)
			{
			  SPI1_ReadWriteByte(*DataBuffer);
			  DataBuffer++;
			}	
	W25QXX_CS=1;                   
	W25QXX_Wait_Busy();					   
}

void W25QXX_PageWrite(u8* DataBuffer,u32 StartAddress,u16 ByteCount)   
{
  u8 NumOfPage = 0, NumOfSingle = 0, Surplus_Addr = 0, Surplus_count = 0, midtemp = 0;

  Surplus_Addr = StartAddress % 256;
  Surplus_count = 256 - Surplus_Addr;
  NumOfPage =  ByteCount / 256;
  NumOfSingle = ByteCount % 256;

  if (Surplus_Addr == 0) 
  {
    if (NumOfPage == 0)   
    {
      W25QXX_Write_Page(DataBuffer, StartAddress, ByteCount);
    }
    else                   
    {
      while (NumOfPage--)
      {
        W25QXX_Write_Page(DataBuffer, StartAddress, 256);
        StartAddress +=  256;
        DataBuffer += 256;
      }
      W25QXX_Write_Page(DataBuffer, StartAddress, NumOfSingle);
    }
  }
  else 
  {
    if (NumOfPage == 0) // ByteCount < 256 
    {
      if (NumOfSingle > Surplus_count)  
      {
        midtemp = NumOfSingle - Surplus_count;
        W25QXX_Write_Page(DataBuffer, StartAddress, Surplus_count);
        StartAddress +=  Surplus_count;
        DataBuffer += Surplus_count;
        W25QXX_Write_Page(DataBuffer, StartAddress, midtemp);
      }
      else
      {
        W25QXX_Write_Page(DataBuffer, StartAddress, ByteCount);
      }
    }
    else //ByteCount > 256 
    {
      ByteCount -= Surplus_count;
      NumOfPage =  ByteCount / 256;
      NumOfSingle = ByteCount % 256;

      W25QXX_Write_Page(DataBuffer, StartAddress, Surplus_count);
      StartAddress +=  Surplus_count;
      DataBuffer += Surplus_count;
      while (NumOfPage--)
      {
        W25QXX_Write_Page(DataBuffer, StartAddress, 256);
        StartAddress +=  256;
        DataBuffer += 256;
      }
      if (NumOfSingle != 0)
      {
        W25QXX_Write_Page(DataBuffer, StartAddress, NumOfSingle);
      }
    }
  }
} 

u8 TS_BUFFER[4096];	
void W25QXX_SectorWrite(u8* DataBuffer,u32 StartAddress,u16 ByteCount)   
{ 
	u32 secaddr;
	u16 secused;
	u16 Surplus_count;	   
 	u16 i=0; 
	
	u8 * Cache_BUF;	   
  Cache_BUF=TS_BUFFER;	  
	
 	secaddr=StartAddress/4096;  
	secused=StartAddress%4096;
	Surplus_count=4096-secused;  

 	if(ByteCount<=Surplus_count)Surplus_count=ByteCount;
	while(1) 
	{	
		W25QXX_Read(Cache_BUF,secaddr*4096,4096);
		      i=0;
		while(Cache_BUF[secused+i]==0XFF)
				{
					i++;
					if(i==Surplus_count)break;
				}
		if(i<Surplus_count)
				{
					W25QXX_Erase_Sector(secaddr);
					for(i=0;i<Surplus_count;i++)	   
					{
						Cache_BUF[i+secused]=DataBuffer[i];	  
					}
					W25QXX_PageWrite(Cache_BUF,secaddr*4096,4096); 

				}
		else
			  W25QXX_PageWrite(DataBuffer,StartAddress,Surplus_count); 				   
		if(ByteCount==Surplus_count)break;
		else
				{
					secaddr++;
					secused=0; 

					DataBuffer+=Surplus_count; 
					StartAddress+=Surplus_count;   
					ByteCount-=Surplus_count;				
					if(ByteCount>4096)Surplus_count=4096;	
					else Surplus_count=ByteCount;			
				}	 
	};
}

